from functools import reduce
from typing import Any, Callable, Iterable, List, Optional, Sequence

from just_pipeline.cases import Case


def execute_case(data: Any, _case: Case):
    """
    执行case

    :param data: 传入数据。
    :param _case: case
    :return: 返回case执行后的结果。
    """
    is_ok = _case.when(data)
    if is_ok and _case.then:
        return pipeline(data)(*_case.then)

    if not is_ok and _case.otherwise:
        return pipeline(data)(*_case.otherwise)

    return data


def pipeline(data=None, cases: Optional[Sequence[Case]] = None):
    """
    流水线调用

    :param data: 传入数据。
    :param cases: 每次执行pipe之前调用所有cases。
    :return: 返回所有管道对data反复处理后的结果。

    :raises: 本函数不触发异常，但pipes中的函数可能会抛出异常。
    """

    def run_pipes(*pipes: Callable[[Any], Any]):
        """
        执行所有管道函数

        :param pipes: 管道函数，用于处理数据，要求最少有一个参数用来处理data。
        :return: 返回所有管道对data反复处理后的结果。
        """
        ret = data
        for func in pipes:
            ret = reduce(execute_case, cases or [], ret)
            ret = func(ret)
        return ret

    return run_pipes


def pipe(f: Callable[[Any], Any], *args, **kwargs):
    """
    管道，包装函数f，延迟调用和首个参数传入到返回值函数中

    :param f: 待处理的数据。
    :return: 函数f的柯里化，仅需传入首个参数。
    """

    def func(data):
        return f(data, *args, **kwargs)

    return func


def pipe_last(f: Callable, *args, **kwargs):
    """
    与pipe相同，但data作为最后一个参数，兼容Python函数式 map、filter、reduce 等

    :param f: 待处理的数据。
    :return: 函数f的柯里化，仅需传入首个参数。
    """

    def func(data):
        return f(*(args + (data,)), **kwargs)

    return func


class Pipeline:
    """列表形式的流水线"""

    def __init__(self, data=None, cases: Optional[Sequence[Case]] = None):
        """
        初始化
        
        :param data: 传入数据。
        :param cases: 每次执行pipe之前调用所有cases。
        """
        self.data = data
        self.cases = cases
        self.pipes: List[Callable[..., Any]] = []

    def __getitem__(self, f):
        if isinstance(f, Iterable):
            self.pipes = list(f)
        else:
            self.pipes = [f]
        return self

    def run(self):
        """
        执行管线中的函数，返回处理data后的结果
        """
        return pipeline(self.data, self.cases)(*self.pipes)
